#pragma once

#include "IEndPoint.h"
#include "IActiveObject.h"
#include "IContractChecker.h"
#include "RoomMessage.h"
#include "SimpleEvent.h"
#include "SimpleCriticalSection.h"
#include "Binding.h"

namespace RoomFW {

//
// Class:	Portal
//
// Description:	Stand-alone endpoint.  "Portal" between ROOM framework and
//				other classes.
//
// Author:	Andrew Sage
//
class Portal
{
public:

	//
	// Construction/Destruction
	//
	Portal(const char* name);
	virtual ~Portal(void);

	//
	// Method:	getName
	//
	// Description:	Get name portal initialized with.  Don't store returned pointer!
	//
	// Parameters:	None.
	//
	// Return:	Name portal initialized with.
	//
	const char* getName() const;

	//
	// Method:	bindTo
	//
	// Description:	Bind to an active object port.
	//
	// Parameters:
	//
	//	object - Object to bind to.
	//
	//	index - Port index to bind to.  Returns replication index.
	//
	// Return:	None.  Throws std::runtime_error on failure.
	//
	void bindTo(IActiveObjectSP object, PortIndex& index)
		throw (std::runtime_error);

	//
	// Method:	unbind
	//
	// Description:	Un-Bind from active object port.
	//
	// Parameters:	None.
	//
	// Return:	Success.  Failure if not bound.
	//
	bool unbind(void);

	//
	// Method:	sendMessage
	//
	// Description:	Send a message to object/port index bound to.
	//
	// Parameters:
	//
	//	messageID - ID of message to send.
	//
	//	payload - Message payload.
	//
	// Return:	Success.
	//
	void sendMessage(MessageID messageID, IPayloadSP payload = IPayloadSP())
		throw (SendMessageError);

	//
	// Method:	invoke
	//
	// Description:	Send a message and wait for a reply.
	//
	// Parameters:
	//
	//	timeout -	Max time in milliseconds to wait for a reply.  If the reply is not received in
	//				this time, it will be lost and a null message will be returned.  This must be
	//				checked and accouted for.  Under no circumstance should you assume that you will
	//				always get a reply.
	//
	//	messageID -	Message ID.
	//
	//	payload -	Message payload.
	//
	// Return:	Reply or null message.  Null message could be that the receiver didn't reply, or there
	//			was a contract violation sending.
	//
	RoomMessageSP invoke(uint32_t timeout, MessageID messageID, IPayloadSP payload = IPayloadSP(), bool throwOnTimeout = true)
		throw (SendMessageError);

	//
	// Method:	hasMessage
	//
	// Description:	Return true if getMessage will return a message.
	//
	// Parameters:	None
	//
	// Return:	True if getMessage will return a message.
	//
	bool hasMessage(void);

	//
	// Method:	getMessage
	//
	// Description:	Get an incoming message that has been queued.  Call until
	//				it returns null!
	//
	// Parameters:	None
	//
	// Return:	Message or null.
	//
	RoomMessageSP getMessage(void);

	//
	// Method:	notifyMessage
	//
	// Description:	Perform some notification that a message has been received.
	//				Derived classes must override.
	//
	// Parameters:	None
	//
	// Return:	None
	//
	virtual void notifyMessage(void) = 0;

	//
	// Method:	dynamicBind
	//
	// Description:	Bind this portal to a port that was registered for dynamic binding.
	//
	// Parameters:
	//
	//	dynamicPortName -	Name of port to bind to.
	//
	//	contractTypeId -	Expected contract type of port.
	//
	//	isConjugate -	Expected role of THIS PORTAL after binding.
	//
	// Return:	Binding pointer.  May be null.
	//
	DynamicBindingSP dynamicBind(const char* dynamicPortName, ContractTypeId contractTypeId, bool isConjugate);

	//
	// Get/Set logging flag.
	//
	bool getIsLogged() const;
	void setIsLogged(bool isLogged);

	//
	// Get protocol.
	//
	IContractCheckerSPC getContractChecker();


private:

	//
	// Class:	PortalEndPoint
	//
	// Description:	Wrapper endpoint to forward message to a portal.
	//
	// Author:	Andrew Sage
	//
	class PortalEndPoint : public IEndPoint
	{
	public:
		PortalEndPoint(Portal* owner) : owner_(owner) {}
		virtual SendMessageError::Reason acceptMessage(
			MessageID messageID,
			IPayloadSP& payload,
			const char* senderName,
			IEndPointSP replyEndpoint = IEndPointSP())
		{
			return owner_->acceptMessage(messageID, payload, senderName, replyEndpoint);
		}
	private:
		Portal* owner_;
	};
	typedef ::boost::shared_ptr<PortalEndPoint> PortalEndPointSP;

	//
	// Accept a message
	//
	SendMessageError::Reason acceptMessage(
		MessageID messageID,
		IPayloadSP& payload,
		const char* senderName,
		IEndPointSP replyEndpoint = IEndPointSP());

	//
	// Check for errors before sending a message.
	//
	void preSend(MessageID messageID, IPayloadSP& payload) const;

	//
	// Pass message to endpoint and throw errors.
	//
	void finalSend(MessageID messageID, IPayloadSP& payload, IEndPointSP replyEndpoint) const;

	//
	// Name of this portal.
	//
	std::string name_;

	//
	// Object bound to.
	//
	IActiveObjectSP peerObj_;

	//
	// Object id / port index bound to.
	//
	PortIndex peerPort_;

	//
	// Outgoing message endpoint.
	//
	IEndPointSP peerEP_;

	//
	// Reply message endpoint.
	//
	IEndPointSP replyEndpoint_;

	//
	// Conjugate of contract checker of port point to.  Used to check incominig messages.
	//
	IContractCheckerSPC contract_;

	//
	// Incoming message queue.
	//
	std::queue<RoomMessageSP> messageQueue_;

	//
	// Synchronize queue access.
	//
	SimpleCriticalSection queueSynch_;

	//
	// Not designed to be copied.
	//
	Portal(const Portal&);
	Portal& operator =(const Portal&);
};
typedef ::boost::shared_ptr<Portal> PortalSP;


//
// Class:	Win32Portal
//
// Description:	Portal that sends a specified windows message as notification.
//
// Author:	Andrew Sage
//
class Win32Portal : public Portal
{
public:

	//
	// Constructor.
	//
	Win32Portal(const char* name, HWND hWnd = 0, UINT messageId = 0, WPARAM wParam = 0, LPARAM lParam = 0);

	//
	// Set notification info.  ::PostMessage() arguement.
	//
	void setNotifyInfo(HWND hWnd, UINT messageId, WPARAM wParam = 0, LPARAM lParam = 0);

	//
	// PostMessage(/*configured args*/) when a message is received.
	//
	virtual void notifyMessage(void);

private:

	//
	// Notification info.
	//
	HWND hWnd_;
	UINT messageId_;
	WPARAM wParam_;
	LPARAM lParam_;
};
typedef ::boost::shared_ptr<Win32Portal> Win32PortalSP;

//
// Class:	EventPortal
//
// Description:	Portal that sets an event as notification.
//
// Author:	Andrew Sage
//
class EventPortal : public Portal
{
public:

	//
	// Constructor
	//
	EventPortal(const char* name);

	//
	// Initialize event.  Return success.
	//
	bool initialize(void);

	//
	// Wait for message received event
	//
	bool wait(uint32_t timeout = INFINITE);

	//
	// Set event when a message is received.
	//
	virtual void notifyMessage(void);

private:

	//
	// Message Event.
	//
	SimpleEvent messageEvent_;
};
typedef ::boost::shared_ptr<EventPortal> EventPortalSP;

//
// Class:	CallbackPortal
//
// Description:	Portal that calls a specified function as notification.
//
// Author:	Andrew Sage
//
class CallbackPortal : public Portal
{
public:

	//
	// Callback interface to be implemented by clients.  Called with this as arg.
	// WARNING:  This is called by the thread of the active object bound to.  Client
	//			implementations must be thread safe!
	//
	struct ICallback
	{
		virtual void notifyMessage(CallbackPortal* portal) = 0;
	};

	//
	// Constructor.
	//
	CallbackPortal(const char* name, ICallback* callback = 0);

	//
	// Set callback.
	//
	void setCallback(ICallback* callback);

	//
	// Call callback->notifyMessage(this);
	//
	virtual void notifyMessage(void);

private:

	//
	// Callback.
	//
	ICallback* callback_;

	//
	// Synchronize callback access.
	//
	SimpleCriticalSection callbackSynch_;

};
typedef ::boost::shared_ptr<CallbackPortal> CallbackPortalSP;


//
// Class:	PortalQueue
//
// Description:	Provides a single wait point for managing multiple callback portals.
//
// Author:	Andrew Sage
//
class PortalQueue : public CallbackPortal::ICallback
{
public:
	PortalQueue();
	bool wait(uint32_t timeout = INFINITE);
	CallbackPortal* pop();
	bool hasMessage();
	virtual void notifyMessage(CallbackPortal* portal);
private:

	std::queue<CallbackPortal*> readyPortals_;
	SimpleCriticalSection queueSynch_;
	SimpleEvent messageEvent_;
};

}
